{
 "cells": [
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    " \n",
    "\n",
    "#### 目录\n",
    "\n",
    "*   [前言](about:blank#_4)\n",
    "*   [一、LangGraph](about:blank#LangGraph_11)\n",
    "*   *   [1-1、介绍](about:blank#11_13)\n",
    "    *   [1-2、特点](about:blank#12_23)\n",
    "    *   [1-3、安装](about:blank#13_47)\n",
    "    *   [1-4、什么是图？](about:blank#14_53)\n",
    "    *   [1-5、为什么选择图？](about:blank#15_80)\n",
    "    *   [1-6、LangGraph应用的简单示例—CRAG（自我改正型RAG）](about:blank#16LangGraphCRAGRAG_104)\n",
    "    *   [1-7、LangGraph基础概念](about:blank#17LangGraph_127)\n",
    "    *   *   [1-7-1、Graphs（图的概念&关键组件&如何构建）](about:blank#171Graphs_129)\n",
    "        *   [1-7-2、State（状态）](about:blank#172State_153)\n",
    "        *   [1-7-3、Annotated（数据类型）](about:blank#173Annotated_163)\n",
    "        *   [1-7-4、Node（节点）](about:blank#174Node_206)\n",
    "        *   [1-7-5、Edge（边）](about:blank#175Edge_247)\n",
    "*   [二、Demo案例介绍](about:blank#Demo_264)\n",
    "*   *   [2-1、案例介绍](about:blank#21_266)\n",
    "    *   [2-2、安装](about:blank#22_274)\n",
    "    *   [2-3、构建一个基础ChatBot](about:blank#23ChatBot_292)\n",
    "    *   *   [2-3-1、状态图的定义](about:blank#231_294)\n",
    "        *   [2-3-2、添加节点&边](about:blank#232_324)\n",
    "        *   [2-3-3、运行](about:blank#233_369)\n",
    "    *   [2-4、使用工具（网络搜索）](about:blank#24_399)\n",
    "    *   *   [2-4-1、依赖安装](about:blank#241_401)\n",
    "        *   [2-4-2、工具定义&简单执行](about:blank#242_412)\n",
    "        *   [2-4-3、定义图&绑定工具](about:blank#243_434)\n",
    "        *   [2-4-4、工具节点定义](about:blank#244_480)\n",
    "        *   [2-4-5、定义条件边](about:blank#245_531)\n",
    "        *   [2-4-6、调用](about:blank#246_567)\n",
    "    *   [2-5、人工介入](about:blank#25_597)\n",
    "*   [总结](about:blank#_680)\n",
    "\n",
    "* * *\n",
    "\n",
    "[](https://blog.csdn.net/weixin_42475060/article/details/144428028)前言\n",
    "---------------------------------------------------------------------\n",
    "\n",
    "LangGraph是一个专注于构建有状态、多角色应用程序的库，它利用大语言模型（LLMs）来创建智能体和多智能体工作流。\n",
    "\n",
    "[](https://blog.csdn.net/weixin_42475060/article/details/144428028)一、LangGraph\n",
    "------------------------------------------------------------------------------\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-1、介绍\n",
    "\n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/24657b3214a84f80be05ca5f19d59d1a.png)\n",
    "\n",
    "**LangGraph是一个专注于构建有状态、多角色应用程序的库，它利用大型语言模型（LLMs）来创建智能体和多智能体工作流。这个框架的核心优势体现在以下几个方面：**\n",
    "\n",
    "*   周期性支持：LangGraph允许开发者定义包含循环的流程，这对于大多数中智能体架构来说至关重要。这种能力使得LangGraph与基于有向无环图（DAG）的解决方案区分开来，因为它能够处理需要重复步骤或反馈循环的复杂任务。\n",
    "*   高度可控性：LangGraph提供了对应用程序流程和状态的精细控制。这种精细控制对于创建行为可靠、符合预期的智能体至关重要，特别是在处理复杂或敏感的应用场景时。\n",
    "*   持久性功能：LangGraph内置了持久性功能，这意味着智能体能够跨交互保持上下文和记忆。这对于实现长期任务的一致性和连续性非常关键。持久性还支持高级的人机交互，允许人类输入无缝集成到工作流程中，并使智能体能够通过记忆功能学习和适应。\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-2、特点\n",
    "\n",
    "**1\\. Cycles and Branching（循环和分支）**\n",
    "\n",
    "*   功能描述：允许在应用程序中实现循环和条件语句。\n",
    "*   应用场景：适用于需要重复执行任务或根据不同条件执行不同操作的场景，如自动化决策流程、复杂业务逻辑处理等。\n",
    "\n",
    "**3\\. Persistence（持久性）**\n",
    "\n",
    "*   功能描述：自动在每个步骤后保存状态，可以在任何点暂停和恢复Graph执行，以支持错误恢复、等。\n",
    "*   应用场景：对于需要中断和恢复的长时任务非常有用，例如数据分析任务、需要人工审核的流程等。\n",
    "\n",
    "**4\\. Human-in-the-Loop**\n",
    "\n",
    "*   功能描述：允许中断Graph的执行，以便人工批准或编辑Agent计划的下一步操作。\n",
    "*   应用场景：在需要人工监督和干预的场合，如敏感操作审批、复杂决策支持等。\n",
    "\n",
    "**5\\. Streaming Support（流支持）**\n",
    "\n",
    "*   功能描述：支持在节点产生输出时实时流输出（包括Token流）。\n",
    "*   应用场景：适用于需要实时数据处理和反馈的场景，如实时数据分析、在线聊天机器人等。\n",
    "\n",
    "**6\\. Integration with LangChain and LangSmith（与LangChain和LangSmith集成）**\n",
    "\n",
    "*   功能描述：LangGraph可以与LangChain和LangSmith无缝集成，但并不强制要求它们。\n",
    "*   应用场景：增强LangChain和LangSmith的功能，提供更灵活的应用构建方式，特别是在需要复杂流程控制和状态管理的场合。\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-3、安装\n",
    "\n",
    "``` shell\n",
    "pip install -U langgraph\n",
    "\n",
    "```\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-4、什么是图？\n",
    "\n",
    "**图（Graph）是数学中的一个基本概念，它由点集合及连接这些点的边集合组成。图主要用于模拟各种实体之间的关系，如网络结构、社会关系、信息流等。以下是图的基本组成部分：**  \n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3125e3a8c8aa47f5818ae1a7b762e68c.png)\n",
    "\n",
    "*   **顶点（Vertex）**：图中的基本单元，通常用来表示实体。在社交网络中，每个顶点可能代表一个人；在交通网络中，每个顶点可能代表一个城市或一个交通枢纽。\n",
    "*   **边（Edge）**：连接两个顶点的线，表示顶点之间的关系或连接。边可以有方向（称为有向图），也可以没有方向（称为无向图）。\n",
    "*   **权重（Weight）**：有时边会被赋予一个数值，称为权重，表示两个顶点之间关系的强度或某种度量，如距离、容量、成本等。\n",
    "\n",
    "**图可以根据边的性质分为以下几种：**\n",
    "\n",
    "*   **无向图**：边没有方向。\n",
    "*   **有向图**：边有方向，通常用箭头表示。\n",
    "*   **简单图**：没有重复的边和顶点自环（即边的两个端点是不同的顶点，且没有边从一个顶点出发又回到同一个顶点）。\n",
    "*   **多重图**：可以有重复的边或顶点自环。\n",
    "*   **连通图**：在无向图中，任意两个顶点之间都存在路径。\n",
    "\n",
    "**图在计算机科学中有广泛的应用，例如：**\n",
    "\n",
    "*   **网络流问题**：如最大流、最小割问题。\n",
    "*   **路径查找问题**：如最短路径、所有路径问题。\n",
    "*   **社交网络分析**：分析社交关系网，识别关键节点等。\n",
    "*   **推荐系统**：通过分析用户之间的关系和偏好来推荐内容。\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-5、为什么选择图？\n",
    "\n",
    "**LangGraph之所以使用“图”这个概念，主要是因为图（Graph）在表达复杂关系和动态流程方面具有天然的优势。以下是使用图概念的一些具体原因：**\n",
    "\n",
    "*   **表达复杂关系**：在构建智能体应用时，各组件之间可能存在复杂的关系和交互。图结构可以很好地表示这些关系，包括节点（代表状态或操作）和边（代表转移或关系）。\n",
    "*   **动态流程管理**：智能体在执行任务时，往往需要根据不同的输入或状态动态调整其行为。图结构允许灵活地表示这些动态流程，如循环、分支和并行路径。\n",
    "*   **可扩展性**：图结构易于扩展。随着应用复杂度的增加，可以轻松地在图中添加新的节点和边，而不需要重写整个流程。\n",
    "*   **可视化**：图的可视化特性使得开发者能够直观地理解和调试智能体的行为。通过图形化的表示，可以更快速地识别问题和优化点。\n",
    "*   **循环和递归**：许多智能体应用需要处理循环或递归逻辑，如图结构可以自然地表示这种循环引用和重复过程。\n",
    "*   **灵活的控制流**：与传统的线性流程（如有向无环图DAG）相比，图结构支持更复杂的控制流，包括条件分支和并发执行。\n",
    "*   **启发式算法和数据流**：图算法（如最短路径、网络流等）可以为优化智能体行为提供启发，特别是在处理数据流和资源分配时。\n",
    "\n",
    "**在LangChain的简答链中无法实现的循环场景：**\n",
    "\n",
    "**1、代码生成与自我纠正：**\n",
    "\n",
    "*   场景描述：利用LLM自动生成软件代码，并根据代码执行的结果进行自我反省和修正。\n",
    "*   LangGraph应用：LangGraph可以创建一个循环流程，首先生成代码，然后测试执行，根据执行结果反馈给LLM，让它重新生成或修正代码，直到达到预期的执行效果。这种循环机制在传统的链式（Chain）结构中难以实现。\n",
    "\n",
    "**2、Web自动化导航：**\n",
    "\n",
    "*   场景描述：自动在Web上进行导航，例如自动填写表单、点击按钮或从网站上抓取信息。\n",
    "*   LangGraph应用：LangGraph可以定义一个包含循环的流程，使得智能体能够在进入下一界面时，根据多模态模型的决定来执行不同的操作（如点击、滚动、输入等），直到完成特定任务。这种循环和条件逻辑的运用在LangGraph中得到了很好的支持。\n",
    "\n",
    "**总结来说：LangGraph可以表达更复杂的关系，更灵活，控制更精细，具备循环能力。**\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-6、LangGraph应用的简单示例—CRAG（自我改正型RAG）\n",
    "\n",
    "> **LangGraph**： 是 LangChain 的扩展库，不是独立的框架。它能协调 Chain、Agent 和 Tool 等组件，支持 LLM 循环调用和 Agent 过程的精细化控制。LangGraph 使用状态图（StateGraph）代替了 AgentExecutor 的黑盒调用，通过定义图的节点和边来详细定义基于 LLM 的任务。在任务运行期间，它会维护一个中央状态对象，该对象会根据节点的变化不断更新，其属性可根据需要进行自定义。相比于 AgentExecutor，LangGraph 可以更加精细的进行控制：\n",
    "\n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/5abadb1dac57431886f4f10a910e81ef.png)\n",
    "\n",
    "**CRAG：** 顾名思义，一种RAG的变体，结合了对检索到的文档的自我反思/自我评分。  \n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/80400f1c491c40919f629cde62e80d41.png)  \n",
    "**图表展示了一个查询处理流程，涉及多个阶段和决策点：**\n",
    "\n",
    "*   Question（提问）：这是整个流程的开始点，用户提出一个问题。\n",
    "*   Retrieve Node（检索节点）：系统尝试从数据库或索引中检索与问题相关的信息。\n",
    "*   Grade Node（评分节点）：对检索到的信息进行评估，判断其相关性或准确性。\n",
    "*   Decision Point（决策点）：根据评分节点的输出，系统会做出是否继续当前路径还是选择替代路径的决定。  \n",
    "    如果没有发现任何无关的文档（“Any doc irrelevant”? “No”），则流程直接跳到“Answer（答案）”节点。  \n",
    "    如果发现了无关的文档（“Any doc irrelevant”? “Yes”），则进入下一个阶段。重查。\n",
    "*   Re-write Query Node（重写查询节点）：由于检索到的某些文档被认为是不相关的，系统会对原始查询进行重新表述，以便更准确地反映用户的需求。\n",
    "*   Web Search Node（网页搜索节点）：使用重写的查询在互联网上搜索更多信息。\n",
    "*   Answer（答案）：最终，系统将生成的答案返回给用户。\n",
    "\n",
    "> 节点可以是可调用的函数、工具、Agent、或者是一个可运行的chain。\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-7、LangGraph基础概念\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-7-1、Graphs（图的概念&关键组件&如何构建）\n",
    "\n",
    "**在LangGraph框架中，“Graphs”（图）是核心概念之一，用于图形化 智能体（agents）的工作流程。（即将工作流程建模为图形），主要使用三个关键组件来定义：**\n",
    "\n",
    "*   State: 状态，一个共享的数据结构。\n",
    "*   Nodes：节点，编辑Agent逻辑的python函数，接收当前状态作为输入，执行一系列计算后，返回更新的状态。\n",
    "*   Edges：边，基于当前状态决定下一个执行节点。边可以是条件分支，或者固定转换。\n",
    "\n",
    "_通过组合、拼接节点和边，可以创建复杂的工作流程。_\n",
    "\n",
    "**Graphs 执行开始时，所有节点都以初始状态开始，当节点完成操作后，它会沿着一条或者多条边向其他节点发送消息，之后，接收方节点执行其函数，将生成的消息传递给下一组节点。直到没有消息传递！**  \n",
    "**简单说：节点完成操作，边决策下一步干什么。**\n",
    "\n",
    "**参数：**\n",
    "\n",
    "*   StateGraph：状态图，用于将用户定义的对象参数化。\n",
    "*   MessageGraph：消息图，除了聊天机器人外基本不使用。\n",
    "\n",
    "**构建图：** 首先需要定义state，之后需要添加各个节点和边，最后就可以编译图了。（对图结构的一些基本检查，确保图没有孤立节点，另外还可以指定一些运行时的参数）。调用以下方法来编译图：\n",
    "\n",
    "``` python\n",
    "graph = graph_builder.compile(...)\n",
    "\n",
    "```\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-7-2、State（状态）\n",
    "\n",
    "> State：\n",
    "\n",
    "1.  **定义：** 状态是Graph中的一个共享数据结构，它存储了所有与当前执行上下文相关的信息。\n",
    "2.  **数据结构：** 状态可以是任何Python类型，通常使用TypedDict或PydanticBaseModel。TypedDict是一个Python字典，它允许对字典键和值的类型进行注解。\n",
    "3.  **作用：** 状态用于存储和传递应用程序的数据，使得节点可以基于这些数据执行操作。它提供了节点之间的通信机制，因为每个节点都可以读取前一个节点更新的状态，并在此基础上进行操作。\n",
    "4.  **管理：** 状态的管理是自动的。当一个节点执行并返回一个更新后的状态时，LangGraph框架会确保这个新状态被传递到下一个节点。\n",
    "5.  **生命周期：** 状态的生命周期与图的执行周期相匹配，它从图的初始状态开始，并在图的每个节点执行时更新，直到图执行结束。\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-7-3、Annotated（数据类型）\n",
    "\n",
    "**Annotated作用：**\n",
    "\n",
    "1.  **元数据添加**：Annotated允许开发者在类型提示中添加额外的信息，这些信息可以被类型检查器、框架或其他工具使用。\n",
    "2.  **类型提示增强**：它提供了一种方式来增强现有的类型提示，而不需要创建新的类型。\n",
    "3.  **代码文档**：Annotated可以作为一种文档形式，提供关于变量、函数参数或返回值的额外信息。\n",
    "\n",
    "**用法1：** DistanceInCm是一个带注释的整数类型。注释 “Units: cm” 说明了这个整数代表的是以厘米为单位的距离。**注释可以用来作为文档，说明变量的用途或期望的值。**\n",
    "\n",
    "``` python\n",
    "from typing import Annotated\n",
    "from typing_extensions import Annotated  # 如果标准库中没有Annotated\n",
    "\n",
    "# 定义一个带注释的整数类型\n",
    "# 这里的 \"Units: cm\" 是一个注释，它不会改变类型的行为\n",
    "DistanceInCm = Annotated[int, \"Units: cm\"]\n",
    "\n",
    "def measure_distance(distance: DistanceInCm) -> DistanceInCm:\n",
    "    # 这里我们假设函数会测量距离，并返回以厘米为单位的距离\n",
    "    # 注意：函数的实现并不关心注释 \"Units: cm\"\n",
    "    return distance\n",
    "\n",
    "# 使用带注释的类型\n",
    "distance: DistanceInCm = 10  # 10 厘米\n",
    "new_distance = measure_distance(distance)\n",
    "\n",
    "```\n",
    "\n",
    "**用法2：** messages 变量，其类型被注解为 Annotated\\[list, add\\_messages\\]。list 表示 messages 键的值应该是一个列表。add\\_messages 是一个函数，它在 Annotated 注解中使用，提供了关于如何更新状态字典中 messages 的额外信息。\n",
    "\n",
    "``` python\n",
    "from typing import Annotated\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class State(TypedDict):\n",
    "    # Messages have the type \"list\". The `add_messages` function\n",
    "    # in the annotation defines how this state key should be updated\n",
    "    # (in this case, it appends messages to the list, rather than overwriting them)\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "```\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-7-4、Node（节点）\n",
    "\n",
    "**Node：** 在LangGraph框架中，节点（Nodes）是Python函数，它们编码了智能体（agents）的逻辑。其中第一个位置参数是State（名称）。第二个位置参数是config（Node对应的处理逻辑）。\n",
    "\n",
    "``` python\n",
    "from langchain_core.runnables import RunnableConfig\n",
    "from langgraph.graph import StateGraph\n",
    "\n",
    "builder = StateGraph(dict)\n",
    "\n",
    "def my_node(state: dict, config: RunnableConfig):\n",
    "    print(\"In node: \", config[\"configurable\"][\"user_id\"])\n",
    "    return {\"results\": f\"Hello, {state['input']}!\"}\n",
    "\n",
    "# The second argument is optional\n",
    "def my_other_node(state: dict):\n",
    "    return state\n",
    "\n",
    "\n",
    "builder.add_node(\"my_node\", my_node)\n",
    "builder.add_node(\"other_node\", my_other_node)\n",
    "...\n",
    "\n",
    "```\n",
    "\n",
    "**Start节点：** 特殊节点，表示用户输入发送到Graph的节点。该节点的主要目的是确定首先应该调用哪些节点。\n",
    "\n",
    "``` python\n",
    "from langgraph.graph import START\n",
    "\n",
    "graph.add_edge(START, \"node_a\")\n",
    "\n",
    "```\n",
    "\n",
    "**End节点：** 特殊节点，确定哪条边完成后，没有后续操作。\n",
    "\n",
    "``` python\n",
    "from langgraph.graph import END\n",
    "\n",
    "graph.add_edge(\"node_a\", END)\n",
    "\n",
    "```\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)1-7-5、Edge（边）\n",
    "\n",
    "**在LangGraph框架中，边（Edges）是用于连接节点的对象，它们定义了节点之间的转换逻辑。每条边都连接两个节点：一个源节点和一个目标节点。边的主要功能是确定何时应该从源节点跳转到目标节点。**\n",
    "\n",
    "*   Normal Edges：正常边，直接从一个节点转到下一个节点。\n",
    "*   Conditional Edges：调用一个函数以确定接下来要转到哪个节点。\n",
    "\n",
    "``` python\n",
    "# 正常边，直接从节点A跳转到节点B\n",
    "graph.add_edge(\"node_a\", \"node_b\")\n",
    "\n",
    "# 条件边，从节点A选择性的跳转到下一条边，routing_function为跳转的逻辑方法。\n",
    "graph.add_conditional_edges(\"node_a\", routing_function)\n",
    "\n",
    "```\n",
    "\n",
    "[](https://blog.csdn.net/weixin_42475060/article/details/144428028)二、Demo案例介绍\n",
    "-----------------------------------------------------------------------------\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-1、案例介绍\n",
    "\n",
    "**我们将会使用LangGraph构建一个聊天机器人，它可以：**\n",
    "\n",
    "*   通过网络搜索来回答常见的问题\n",
    "*   将复杂的查询路由给人工审阅\n",
    "*   使用自定义状态来控制其行为\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-2、安装\n",
    "\n",
    "``` shell\n",
    "pip install -U langgraph langsmith langchain_anthropic\n",
    "\n",
    "```\n",
    "\n",
    "**这里的大模型我们使用通义千问：**\n",
    "\n",
    "``` python\n",
    "import os\n",
    "from langchain_community.llms import Tongyi\n",
    "\n",
    "llm = Tongyi(\n",
    "    model='qwen-max',\n",
    "    api_key=os.environ.get('DASHSCOPE_API_KEY')\n",
    ")\n",
    "\n",
    "```\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-3、构建一个基础ChatBot\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-3-1、状态图的定义\n",
    "\n",
    "**通过创建一个基础聊天机器人来熟悉LangGraph的核心概念：**\n",
    "\n",
    "``` python\n",
    "from typing import Annotated\n",
    "\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "\n",
    "class State(TypedDict):\n",
    "    # Messages have the type \"list\". The `add_messages` function\n",
    "    # in the annotation defines how this state key should be updated\n",
    "    # (in this case, it appends messages to the list, rather than overwriting them)\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "```\n",
    "\n",
    "**StateGraph介绍：** StateGraph 是在 langgraph 这个库中的一个类，它用于表示和处理状态图。状态图是一种特殊的图，它由节点（或状态）和边（或转移）组成，用于模拟系统的行为或流程。在 langgraph 的上下文中，StateGraph 通常用于定义和执行基于语言模型（如大型语言模型 LLM）的任务流程。以下是 StateGraph 的一些基本特征和用途：\n",
    "\n",
    "*   **节点（States）**：在 StateGraph 中，每个节点代表系统的一个状态。这些状态可以是任何可以定义的状态，比如任务的开始、中间步骤或结束。\n",
    "*   **边（Transitions）**：边代表从一个状态到另一个状态的转移。这些转移通常由某些条件或操作触发。\n",
    "*   **流程控制**：StateGraph 可以用来定义复杂的工作流程，其中每个状态可以执行特定的操作，并根据执行结果决定下一步转移到哪个状态。\n",
    "*   **状态管理**：StateGraph 维护一个中央状态对象，这个对象包含了当前执行状态的所有相关信息。这个状态可以在图中的节点之间传递和更新。\n",
    "*   **自定义属性**：StateGraph 允许用户自定义状态和转移的属性，这样就可以根据具体的应用需求来调整和优化流程。\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-3-2、添加节点&边\n",
    "\n",
    "**节点**：这里添加LLM响应作为节点。  \n",
    "**边：** 添加开始边（告诉图，什么时候开始工作）和结束边（告诉图运行什么节点后，退出）。\n",
    "\n",
    "``` python\n",
    "import os\n",
    "from langchain_community.llms import Tongyi\n",
    "\n",
    "llm = Tongyi(\n",
    "    model='qwen-max',\n",
    "    api_key=os.environ.get('DASHSCOPE_API_KEY')\n",
    ")\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm.invoke(state[\"messages\"])]}\n",
    "\n",
    "# The first argument is the unique node name\n",
    "# The second argument is the function or object that will be called whenever\n",
    "# the node is used.\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "graph_builder.add_edge(\"chatbot\", END)\n",
    "graph = graph_builder.compile()\n",
    "\n",
    "# 编译之后可以直接调用invoke函数来执行图。\n",
    "# graph.invoke({\"messages\": [(\"user\", '你好')]})\n",
    "\n",
    "# 直接获取到回复的答案，不包含中间过程。\n",
    "# graph.invoke({\"messages\": [(\"user\", '你是干什么的？')]})['messages'][-1].content\n",
    "\n",
    "```\n",
    "\n",
    "**可视化(在jupyter上进行操作)：**\n",
    "\n",
    "``` python\n",
    "from IPython.display import Image, display\n",
    "\n",
    "try:\n",
    "    display(Image(graph.get_graph().draw_mermaid_png()))\n",
    "except Exception:\n",
    "    # This requires some extra dependencies and is optional\n",
    "    pass\n",
    "\n",
    "```\n",
    "\n",
    "**输出如下所示：**  \n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/398565e184d3432aa2f28b576be135af.png)\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-3-3、运行\n",
    "\n",
    "**Notice：** 构建循环语句去循环执行图。这里需要注意不同LLM的调用，以及返回数据的获取方法。\n",
    "\n",
    "``` python\n",
    "def stream_graph_updates(user_input: str):\n",
    "    for event in graph.stream({\"messages\": [(\"user\", user_input)]}):\n",
    "        for value in event.values():\n",
    "            print(\"Assistant:\", value[\"messages\"][-1])\n",
    "\n",
    "while True:\n",
    "    try:\n",
    "        user_input = input(\"User: \")\n",
    "        if user_input.lower() in [\"quit\", \"exit\", \"q\"]:\n",
    "            print(\"Goodbye!\")\n",
    "            break\n",
    "\n",
    "        stream_graph_updates(user_input)\n",
    "    except:\n",
    "        # fallback if input() is not available\n",
    "        user_input = \"What do you know about LangGraph?\"\n",
    "        print(\"User: \" + user_input)\n",
    "        stream_graph_updates(user_input)\n",
    "        break\n",
    "\n",
    "\n",
    "```\n",
    "\n",
    "**输出结果：**  \n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/594a1098f37049ddae58e0ff1264dcfa.png)\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4、使用工具（网络搜索）\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4-1、依赖安装\n",
    "\n",
    "**tavily 搜索API申请地址**: [https://docs.tavily.com/docs/rest-api/api-reference](https://docs.tavily.com/docs/rest-api/api-reference)\n",
    "\n",
    "``` shell\n",
    "pip install -U langchain-community tavily-python\n",
    "\n",
    "```\n",
    "\n",
    "**TavilySearchResults参数介绍**\n",
    "\n",
    "*   max\\_results：最大返回搜索数量\n",
    "*   include\\_answer：是否包含答案\n",
    "*   include\\_images： 是否包含图片\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4-2、工具定义&简单执行\n",
    "\n",
    "``` python\n",
    "import os \n",
    "\n",
    "os.environ[\"TAVILY_API_KEY\"] = \"\"\n",
    "\n",
    "from langchain_community.tools import TavilySearchResults\n",
    "\n",
    "tool = TavilySearchResults(\n",
    "    max_results=5,\n",
    "    include_answer=True,\n",
    "    include_raw_content=True,\n",
    "    include_images=True,\n",
    "    # search_depth=\"advanced\",\n",
    "    # include_domains = []\n",
    "    # exclude_domains = []\n",
    ")\n",
    "tools = [tool]\n",
    "tool.invoke({'query': '谁是世界上最美丽的女人？'})\n",
    "\n",
    "```\n",
    "\n",
    "**输出：**  \n",
    "![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9f516cfe5f5b4b72b329a76ea8ebec96.png)\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4-3、定义图&绑定工具\n",
    "\n",
    "**Guide：**\n",
    "\n",
    "*   定义图\n",
    "*   使用通义千问模型qwen-max，这里是通过ChatOpenAI包装。\n",
    "*   绑定工具\n",
    "\n",
    "``` python\n",
    "from typing import Annotated\n",
    "\n",
    "from langchain_anthropic import ChatAnthropic\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START, END\n",
    "from langgraph.graph.message import add_messages\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "\n",
    "graph_builder = StateGraph(State)\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    model=\"qwen-max\",\n",
    "    temperature=0,\n",
    "    max_tokens=1024,\n",
    "    timeout=None,\n",
    "    max_retries=2,\n",
    "    api_key=os.environ.get('DASHSCOPE_API_KEY'),\n",
    "    base_url=\"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "# Modification: tell the LLM which tools it can call\n",
    "llm_with_tools = llm.bind_tools(tools)\n",
    "\n",
    "def chatbot(state: State):\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "graph_builder.add_node(\"chatbot\", chatbot)\n",
    "\n",
    "```\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4-4、工具节点定义\n",
    "\n",
    "**Guide：**\n",
    "\n",
    "*   创建工具节点类\n",
    "*   添加工具节点\n",
    "\n",
    "``` python\n",
    "import json\n",
    "\n",
    "from langchain_core.messages import ToolMessage\n",
    "\n",
    "class BasicToolNode:\n",
    "    \"\"\"A node that runs the tools requested in the last AIMessage.\"\"\"\n",
    "\t\n",
    "\t# 首先，初始化传入的tools应该为列表\n",
    "    def __init__(self, tools: list) -> None:\n",
    "    \t# 对于所有工具，都构建字典\n",
    "        self.tools_by_name = {tool.name: tool for tool in tools}\n",
    "\n",
    "    def __call__(self, inputs: dict):\n",
    "    \t# 条件语句，如果inputs.get(\"messages\", [])返回的值不为空，则获取到最后一条消息(AI Message)。\n",
    "    \t# 否则，报错\n",
    "    \t# inputs.get(\"messages\", [])：使用.get() 方法尝试从名为inputs的字典中获取键为“messages”的值，如果不存在，则返回一个空列表[]。\n",
    "        if messages := inputs.get(\"messages\", []):\n",
    "            message = messages[-1]\n",
    "        else:\n",
    "            raise ValueError(\"No message found in input\")\n",
    "        outputs = []\n",
    "        # 详细的工具调用信息\n",
    "        for tool_call in message.tool_calls:\n",
    "        #  调用相应的工具\n",
    "            tool_result = self.tools_by_name[tool_call[\"name\"]].invoke(\n",
    "                tool_call[\"args\"]\n",
    "            )\n",
    "            outputs.append(\n",
    "                ToolMessage(\n",
    "                    content=json.dumps(tool_result),\n",
    "                    name=tool_call[\"name\"],\n",
    "                    tool_call_id=tool_call[\"id\"],\n",
    "                )\n",
    "            )\n",
    "        return {\"messages\": outputs}\n",
    "\n",
    "tool_node = BasicToolNode(tools=[tool])\n",
    "graph_builder.add_node(\"tools\", tool_node)\n",
    "\n",
    "```\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4-5、定义条件边\n",
    "\n",
    "**Guide：** 用于条件边选择，如果最后一条消息包含工具调用，则调用相关工具，如果没有，则结束。\n",
    "\n",
    "``` python\n",
    "from typing import Literal\n",
    "\n",
    "def route_tools(\n",
    "    state: State,\n",
    "):\n",
    "    \"\"\"\n",
    "    Use in the conditional_edge to route to the ToolNode if the last message\n",
    "    has tool calls. Otherwise, route to the end.\n",
    "    \"\"\"\n",
    "    if isinstance(state, list):\n",
    "        ai_message = state[-1]\n",
    "    elif messages := state.get(\"messages\", []):\n",
    "        ai_message = messages[-1]\n",
    "    else:\n",
    "        raise ValueError(f\"No messages found in input state to tool_edge: {state}\")\n",
    "    # 检查ai_message对象中是否有名为tool_calls 的属性，并且是非空列表，如果是的话，返回字符串“tools”，即路由到工具调用的节点。\n",
    "    if hasattr(ai_message, \"tool_calls\") and len(ai_message.tool_calls) > 0:\n",
    "        return \"tools\"\n",
    "    return END\n",
    "\n",
    "graph_builder.add_conditional_edges(\n",
    "    \"chatbot\",\n",
    "    route_tools,\n",
    "    {\"tools\": \"tools\", END: END},\n",
    ")\n",
    "# Any time a tool is called, we return to the chatbot to decide the next step\n",
    "graph_builder.add_edge(\"tools\", \"chatbot\")\n",
    "graph_builder.add_edge(START, \"chatbot\")\n",
    "graph = graph_builder.compile()\n",
    "\n",
    "```\n",
    "\n",
    "#### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-4-6、调用\n",
    "\n",
    "**Guide：** 到这里为止，我们的Graph就构建完成了。\n",
    "\n",
    "**直接调用：** graph.invoke，立即执行。\n",
    "\n",
    "``` python\n",
    "graph.invoke({\"messages\": [(\"user\", '天天都需要你的爱，这首歌词对应的歌名是什么？')]})\n",
    "\n",
    "```\n",
    "\n",
    "**流式输出：** graph.stream，流式处理，延迟执行，流式处理可以节省内存，不需要一次性的将整个图加载到内存中去。\n",
    "\n",
    "*   config：配置线程，在第二个参数位置。通过config参数，可以保持对话状态。\n",
    "\n",
    "``` python\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "\n",
    "user_input = \"Hi there! My name is Will.\"\n",
    "\n",
    "# The config is the **second positional argument** to stream() or invoke()!\n",
    "events = graph.stream(\n",
    "    {\"messages\": [(\"user\", user_input)]}, config, stream_mode=\"values\"\n",
    ")\n",
    "for event in events:\n",
    "    event[\"messages\"][-1].pretty_print()\n",
    "\n",
    "```\n",
    "\n",
    "**输出：**  \n",
    "_\\================================ Human Message =================================  \n",
    "Hi there! My name is Will.  \n",
    "\\================================== Ai Message ==================================  \n",
    "Hello Will! It’s nice to meet you. How can I assist you today?_\n",
    "\n",
    "### [](https://blog.csdn.net/weixin_42475060/article/details/144428028)2-5、人工介入\n",
    "\n",
    "> **人工介入**: Agent并不是每次都可靠，经过人工审核会更加有保障。在2-4的基础上，只需要在Graph编译时，增加记忆，以及参数interrupt\\_before，就可以得到执行某个节点前一步的结果\n",
    "\n",
    "``` python\n",
    "from langgraph.checkpoint.memory import MemorySaver\n",
    "\n",
    "graph = graph_builder.compile(\n",
    "    checkpointer=memory,\n",
    "    # This is new!\n",
    "    interrupt_before=[\"tools\"],\n",
    "    # Note: can also interrupt __after__ tools, if desired.\n",
    "    # interrupt_after=[\"tools\"]\n",
    ")\n",
    "\n",
    "user_input = \"I'm learning LangGraph. Could you do some research on it for me?\"\n",
    "config = {\"configurable\": {\"thread_id\": \"1\"}}\n",
    "# The config is the **second positional argument** to stream() or invoke()!\n",
    "events = graph.stream(\n",
    "    {\"messages\": [(\"user\", user_input)]}, config, stream_mode=\"values\"\n",
    ")\n",
    "for event in events:\n",
    "    if \"messages\" in event:\n",
    "        event[\"messages\"][-1].pretty_print()\n",
    "\n",
    "```\n",
    "\n",
    "**输出：**  \n",
    "_\\================================ Human Message =================================  \n",
    "I’m learning LangGraph. Could you do some research on it for me?  \n",
    "\\================================== Ai Message ==================================  \n",
    "Tool Calls:  \n",
    "tavily\\_search\\_results\\_json (call\\_b6310d8a4d52445f8fbae8)  \n",
    "Call ID: call\\_b6310d8a4d52445f8fbae8  \n",
    "Args:  \n",
    "query: LangGraph_\n",
    "\n",
    "**Notice:** 如果不传入新的消息，那么调用 graph.stream会从之前中断的地方继续。\n",
    "\n",
    "```\n",
    "events = graph.stream(None, config, stream_mode=\"values\")\n",
    "for event in events:\n",
    "    if \"messages\" in event:\n",
    "        event[\"messages\"][-1].pretty_print()\n",
    "\n",
    "```\n",
    "\n",
    "**输出：**  \n",
    "\\================================== Ai Message ==================================  \n",
    "Tool Calls:  \n",
    "tavily\\_search\\_results\\_json (call\\_dbaf1f15678b4e5aa408c1)  \n",
    "Call ID: call\\_dbaf1f15678b4e5aa408c1  \n",
    "Args:  \n",
    "query: LangGraph  \n",
    "\\================================= Tool Message =================================  \n",
    "Name: tavily\\_search\\_results\\_json\n",
    "\n",
    "\\[{“url”: “https://www.langchain.com/langgraph”, “content”: “LangGraph is a framework for building and scaling agentic applications with LangChain Platform. It supports diverse control flows, human-agent collaboration, streaming, and deployment options for complex tasks.”}, {“url”: “https://langchain-ai.github.io/langgraph/tutorials/”, “content”: “LangGraph is a framework for building and deploying language agents as graphs. Learn how to use LangGraph to create chatbots, RAGs, SQL agents, planning agents, and more with these notebooks.”}, {“url”: “https://medium.com/@cplog/introduction-to-langgraph-a-beginners-guide-14f9be027141”, “content”: “LangGraph is a versatile tool for building complex, stateful applications with LLMs. By understanding its core concepts and working through simple examples, beginners can start to leverage its”}, {“url”: “https://academy.langchain.com/courses/intro-to-langgraph”, “content”: “Separate from the LangChain package, LangGraph helps developers add better precision and control into agentic workflows. Lesson 1: Motivation Lesson 2: Simple Graph Lesson 3: LangGraph Studio Lesson 4: Chain Lesson 5: Router Lesson 6: Agent Lesson 7: Agent with Memory Lesson 8: Deployment Lesson 1: State Schema Lesson 2: State Reducers Lesson 3: Multiple Schemas Lesson 1: Streaming Lesson 2: Breakpoints Lesson 3: Editing State and Human Feedback Lesson 4: Dynamic Breakpoints Lesson 1: Parallelization Lesson 2: Sub-graphs Lesson 3: Map-reduce Lesson 4: Research Assistant About this course No. LangGraph is an orchestration framework for complex agentic systems and is more low-level and controllable than LangChain agents. Deploy LangGraph agents at scale with LangGraph Cloud (available for Python).”}, {“url”: “https://langchain-ai.github.io/langgraphjs/”, “content”: “LangGraph.js is a framework for creating stateful, multi-actor applications with LLMs, using cycles, controllability, and persistence. Learn how to define graphs, tools, and memory for agent and multi-agent workflows with examples and documentation.”}\\]  \n",
    "\\================================== Ai Message ==================================\n",
    "\n",
    "LangGraph is a framework designed for building and scaling agentic applications, particularly with the LangChain Platform. It supports diverse control flows, human-agent collaboration, streaming, and various deployment options for handling complex tasks. Here are some key points from my research:\n",
    "\n",
    "*   **Framework for Language Agents**: LangGraph allows you to build and deploy language agents as graphs, which can be used to create chatbots, Retrieval-Augmented Generation (RAG) systems, SQL agents, planning agents, and more. [Documentation & Tutorials](https://langchain-ai.github.io/langgraph/tutorials/)\n",
    "    \n",
    "*   **Versatile and Stateful Applications**: It’s suitable for creating complex, stateful applications with large language models (LLMs). By understanding its core concepts and working through simple examples, beginners can start to leverage its capabilities. [Beginner’s Guide](https://medium.com/@cplog/introduction-to-langgraph-a-beginners-guide-14f9be027141)\n",
    "    \n",
    "*   **Educational Courses**: There are courses available that introduce LangGraph, covering motivation, simple graph construction, LangGraph Studio, chains, routers, agents, memory, and deployment. [Intro to LangGraph Course](https://academy.langchain.com/courses/intro-to-langgraph)\n",
    "    \n",
    "*   **Orchestration and Control**: LangGraph is an orchestration framework that provides better precision and control into agentic workflows compared to the LangChain package. It’s more low-level and controllable, allowing for the creation of sophisticated agent-based systems. [LangGraph vs. LangChain Agents](https://academy.langchain.com/courses/intro-to-langgraph)\n",
    "    \n",
    "*   **LangGraph.js**: For those interested in JavaScript, there is also a version called LangGraph.js, which is a framework for creating stateful, multi-actor applications with LLMs. It emphasizes cycles, controllability, and persistence. [LangGraph.js Documentation](https://langchain-ai.github.io/langgraphjs/)\n",
    "    \n",
    "\n",
    "If you’re looking to learn LangGraph, it would be beneficial to go through the tutorials and courses linked above, as they provide hands-on experience and in-depth knowledge about the tool.\n",
    "\n",
    "**参考文章：**  \n",
    "[LlamaIndex 官方文档](https://docs.llamaindex.ai/en/stable/)  \n",
    "[langgraph官方教程](https://langchain-ai.github.io/langgraph/tutorials/)  \n",
    "[langgraph操作指南](https://langchain-ai.github.io/langgraph/how-tos/)  \n",
    "[langgraph概念指南](https://langchain-ai.github.io/langgraph/concepts/high_level/)  \n",
    "[langgraph API 参考](https://langchain-ai.github.io/langgraph/reference/graphs/)  \n",
    "[langgraph 词汇表](https://langchain-ai.github.io/langgraph/concepts/low_level/#streaming)  \n",
    "[langgraph 快速入门](https://langchain-ai.github.io/langgraph/tutorials/introduction/)  \n",
    "[彻底搞懂LangGraph【1】：构建复杂智能体应用的LangChain新利器](https://blog.csdn.net/juan9872/article/details/137658555)  \n",
    "[LangChain 79 LangGraph 从入门到精通一](https://blog.csdn.net/zgpeace/article/details/136018212)\n",
    "\n",
    "[](https://blog.csdn.net/weixin_42475060/article/details/144428028)总结\n",
    "---------------------------------------------------------------------\n",
    "\n",
    "**Congratulations!** 你已经完成了LangGraph的快速入门！🎇\n",
    "\n",
    " \n",
    "\n",
    "  \n",
    "\n",
    "本文转自 [https://blog.csdn.net/weixin\\_42475060/article/details/144428028](https://blog.csdn.net/weixin_42475060/article/details/144428028)，如有侵权，请联系删除。"
   ],
   "id": "7975f8ab61992f0a"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "",
   "id": "e6a266f33fe2c987"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
